package com.kvn.dal.core.retry.afterward;

import com.alibaba.fastjson.JSON;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ListMultimap;
import com.google.common.collect.Maps;
import com.google.common.collect.Multimaps;
import com.kvn.dal.core.TimedTask;
import com.kvn.dal.core.dao.IJobRetryDao;
import com.kvn.dal.core.pojo.JobRetry;
import com.kvn.dal.core.retry.RetryParam;
import com.kvn.dal.core.task.ExecutableTask;
import org.apache.commons.lang3.StringUtils;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.util.List;

/**
 * 重试分发调度
* @author wzy
* @date 2017年6月23日 下午5:30:49
*/
@TimedTask(cron = "${afterwardRetry.cron}", desc = "dal-job重试分发调度")
public class AfterwardRetryDispatchJob implements ExecutableTask {
	private static final Logger logger = LoggerFactory.getLogger(AfterwardRetryDispatchJob.class);
	
	@Resource
	private IJobRetryDao jobRetryDao;
	@Resource
	private ApplicationContext applicationContext;

	@Override
	public void execute(JobExecutionContext context) throws JobExecutionException {
		logger.info("AfterwardRetryDispatchJob --> RETRY START.....");
		// 1. 取重试数据依次分发
		List<JobRetry> retryLs = jobRetryDao.getNeedRetryList();
		
		if(CollectionUtils.isEmpty(retryLs)){
			logger.info("AfterwardRetryDispatchJob --> 没有需要重试的记录....exit....");
			return;
		}
		
		long start = System.currentTimeMillis();
		for(JobRetry retry : retryLs){
			try {
				doDispatch(retry);
			} catch (Exception e) {
				//  被重试的接口抛出异常
				handleRetryExceptionResult(retry, e);
				// 重试记录之间互不影响，直到没有待重试的记录为止
			}
		}
		long end = System.currentTimeMillis();
		
		logger.info("AfterwardRetryDispatchJob --> 本次重试结束，总共耗时[{}ms].......", (end - start));
	}

	private void handleRetryExceptionResult(JobRetry retry, Exception e) {
		logger.error("AfterwardRetryDispatchJob --> 被重试的接口[{}]抛出异常，重试失败...", retry.getRetryClassName(), e);
		String errMsg = e.getMessage().length() > 120 ? e.getMessage().substring(0, 120) : e.getMessage();
		jobRetryDao.updateBugResult(retry.getId(), errMsg);
	}

	/**
	 * 分发到对应的重试接口
	 * @param retry
	 */
	private synchronized void doDispatch(JobRetry retry) throws Exception {
		String className = retry.getRetryClassName();
		Class<?> retryClass = null;
		
		try {
			retryClass = Class.forName(className);
		} catch (ClassNotFoundException e) {
			logger.info("AfterwardRetryDispatchJob --> 找不到重试的class=[{}]", className, e);
			throw e;
		}
		
		IRetrySupport retrySupport = (IRetrySupport) applicationContext.getBean(retryClass);
		AfterwardRetryContext retryContext = buildRetryContext(retry);
		long start = System.currentTimeMillis();
		logger.info("AfterwardRetryDispatchJob --> 被重试接口：{}", retrySupport.getClass().getName());
		Boolean retryResult = retrySupport.retry(retryContext);
		long end = System.currentTimeMillis();
		logger.info("AfterwardRetryDispatchJob --> 被重试接口：{}，重试耗时[{}ms]", retrySupport.getClass().getName(), (end - start));
		
		handleRetryNormalResult(retryResult, retry);
	}

	private void handleRetryNormalResult(Boolean retryResult, JobRetry retry) {
		// 更新重试次数和重试记录状态 
		jobRetryDao.updateResult(retry.getId(), retryResult);
	}

	private AfterwardRetryContext buildRetryContext(JobRetry retry) {
		AfterwardRetryContext context = new AfterwardRetryContext();
		context.setJobRetryRecord(retry);
		context.setRetryDataKey(retry.getRetryDataKey());
		String retryParams = retry.getRetryParams();
		if(StringUtils.isNotBlank(retryParams)){
			List<RetryParam> retryParamLs = JSON.parseArray(retryParams, RetryParam.class);
			context.setRetryParamLs(retryParamLs);
			
			ImmutableListMultimap<Class<?>, RetryParam> retryMap = Multimaps.index(retryParamLs.iterator(), new Function<RetryParam, Class<?>>() {
				@Override
				public Class<?> apply(RetryParam input) {
					return input.getParamClass();
				}
			});
			
			ListMultimap<Class<?>, Object> retryParamValueMap = Multimaps.transformEntries(retryMap, new Maps.EntryTransformer<Class<?>, RetryParam, Object>() {
				@Override
				public Object transformEntry(Class<?> key, RetryParam value) {
					return value.retoreParam(key);
				}
			});
			
			context.setRetryParamValueMap(retryParamValueMap);
		}
		return context;
	}

}
